package znet

import (
	"errors"
	"fmt"
	"github.com/zinx/ziface"
	"io"
	"net"
)

// 连接模块
type Connection struct {
	// 当前连接的socket TCP 套接字
	Conn *net.TCPConn
	// 连接ID
	ConnID uint32
	// 当前的连接状态
	isClosed bool
	// 告诉当前连接已经退出的/停止 channel
	ExitChan chan bool
	// 无缓冲的channel，用于读写goroutine之间的消息
	msgChan chan []byte
	// 该连接处理的方法
	MsgHandler ziface.IMsgHandle
}

// 初始化连接模块的方法
func NewConnection(conn *net.TCPConn, connID uint32, msgHandler ziface.IMsgHandle) *Connection {
	return &Connection{
		Conn:       conn,
		ConnID:     connID,
		isClosed:   false,
		MsgHandler: msgHandler,
		ExitChan:   make(chan bool, 1),
		msgChan:    make(chan []byte),
	}
}

func (c *Connection) StartReader() {
	fmt.Println("StartReader Goroutine is running...")
	defer fmt.Println("ConnID = ", c.ConnID, "Reader is exit, remote addr is ", c.RemoteAddr().String())
	defer c.Stop()

	for {

		// 创建拆包解包的对象
		dp := NewDataPack()
		headData := make([]byte, dp.GetHeadLen())
		if _, err := io.ReadFull(c.GetTCPConnection(), headData); err != nil {
			fmt.Println("read message head err", err)
			break
		}
		msg, err := dp.Unpack(headData)
		if err != nil {
			fmt.Println("unpack err:", err)
			break
		}
		var data []byte
		if msg.GetMsgLen() > 0 {
			data = make([]byte, msg.GetMsgLen())
			if _, err := io.ReadFull(c.GetTCPConnection(), data); err != nil {
				fmt.Println("msg data err:", err)
				break
			}
		}
		msg.SetData(data)
		// 得到当前conn数据的Request请求
		req := Request{
			conn: c,
			msg:  msg,
		}
		// 根据绑定好的msgid找到对应的路由执行
		go c.MsgHandler.DoMsgHandler(&req)

	}
}

// 写消息goroutine, 专门发送给客户端消息
func (c *Connection) StartWriter() {
	fmt.Println("Write goroutine is running")
	defer fmt.Println(c.RemoteAddr().String(), "conn writer exit")
	// 不断阻塞等待channel消息，一旦有消息写客户端
	for {
		select {
		case data := <-c.msgChan:
			// 有数据写入客户端
			if _, err := c.Conn.Write(data); err != nil {
				fmt.Println("send data err:", err)
				return
			}
		case <-c.ExitChan:
			// 代表reader已经推出，此时writer已经退出
			return
		}
	}
}

// 将发送给客户端数据封包
func (c *Connection) SendMsg(msgId uint32, data []byte) error {
	if c.isClosed {
		return errors.New("connection is closed when send msg")
	}
	// 将data封包
	dp := NewDataPack()
	binaryMsg, err := dp.Pack(NewMsgPackage(msgId, data))
	if err != nil {
		fmt.Println("sendMsg err:", err)
	}

	// 将数据发送管道
	c.msgChan <- binaryMsg

	return nil
}

// 启动连接 让当前的连接贮备工作
func (c *Connection) Start() {
	fmt.Println("Conn Start() ... ConnID", c.ConnID)
	// 启动从当前连接的读数据业务
	go c.StartReader()
	go c.StartWriter()
}

// 停止连接 结束当前连接的工作
func (c *Connection) Stop() {
	fmt.Println("Conn Stop() ... ConnID", c.ConnID)
	// 如果当前连接已经关闭
	if c.isClosed == true {
		return
	}
	c.isClosed = true
	// 关闭socket连接
	c.Conn.Close()
	// 告知write关闭
	c.ExitChan <- true
	// 关闭管道
	close(c.ExitChan)
	close(c.msgChan)
}

// 获取当前连接的绑定的socket conn
func (c *Connection) GetTCPConnection() *net.TCPConn {
	return c.Conn
}

// 获取当前连接模块的连接id
func (c *Connection) GetConnID() uint32 {
	return c.ConnID
}

// 获取远程客户端的 tcp 状态 IP port
func (c *Connection) RemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}
